from __future__ import annotations

import re
import uuid
from dataclasses import dataclass, asdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, Iterable, Iterator, List, Optional

from .memu_integration import MemoryFileManager


SENT_START = "==== aw:event v1"
SENT_END = "==== end ===="


@dataclass
class Event:
    id: str
    type: str  # post|comment|reaction|footprint
    owner: str
    actor: str
    created_at: str
    headers: Dict[str, str]
    content: str


def now_iso() -> str:
    return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")


def new_event_id(ts: Optional[str] = None) -> str:
    t = ts or now_iso()
    nonce = uuid.uuid4().hex[:6]
    return f"{t}-{nonce}"


def format_block(ev: Event) -> str:
    head_items = [
        f"type={ev.type}",
        f"id={ev.id}",
        f"owner={ev.owner}",
        f"actor={ev.actor}",
    ]
    head = f"{SENT_START} " + " ".join(head_items) + " ===="
    meta_lines = [f"created_at: {ev.created_at}"]
    # write headers deterministically (sorted by key)
    for k in sorted(ev.headers.keys()):
        meta_lines.append(f"{k}: {ev.headers[k]}")
    body = ev.content.rstrip("\n")
    parts = [head, "\n".join(meta_lines), "---", body, "---", SENT_END]
    return "\n".join(parts) + "\n"


# Non-greedy body to avoid spanning multiple blocks
BLOCK_RE = re.compile(
    r"^==== aw:event v1 (?P<meta>[^\n]+) ====\n"  # header line only
    r"(?P<headers>.*?)(?:^---\n)"                  # headers until a line that is exactly '---'
    r"(?P<body>.*?)(?:^---\n)"                     # body until the next '---' line
    r"==== end ====\n?",
    re.S | re.M,
)


def parse_blocks(text: str) -> Iterator[Event]:
    for m in BLOCK_RE.finditer(text):
        meta_str = m.group("meta").strip()
        headers_str = m.group("headers")
        body = m.group("body")

        meta: Dict[str, str] = {}
        for token in meta_str.split():
            if "=" in token:
                k, v = token.split("=", 1)
                meta[k] = v

        headers: Dict[str, str] = {}
        for line in headers_str.splitlines():
            if ":" in line:
                k, v = line.split(":", 1)
                headers[k.strip()] = v.strip()

        yield Event(
            id=meta.get("id", ""),
            type=meta.get("type", ""),
            owner=meta.get("owner", ""),
            actor=meta.get("actor", ""),
            created_at=headers.get("created_at", ""),
            headers={k: v for k, v in headers.items() if k != "created_at"},
            content=body,
        )


def append_event(
    memory_dir: Path,
    owner: str,
    actor: str,
    ev_type: str,
    content: str,
    headers: Optional[Dict[str, str]] = None,
    *,
    created_at: Optional[str] = None,
    event_id: Optional[str] = None,
) -> Event:
    """Append one event into owner/actor/activity.md via MemoryFileManager."""
    headers = headers or {}
    ev = Event(
        id=event_id or new_event_id(),
        type=ev_type,
        owner=owner,
        actor=actor,
        created_at=created_at or now_iso(),
        headers=headers,
        content=content,
    )

    mfm = MemoryFileManager(str(memory_dir), agent_id=owner, user_id=actor)
    existing = mfm.read_memory_file("activity")
    block = format_block(ev)
    new_text = (existing + "\n" if existing and not existing.endswith("\n") else existing) + block
    mfm.write_memory_file("activity", new_text)
    return ev


def event_to_dict(ev: Event) -> Dict[str, str]:
    d = {
        "id": ev.id,
        "type": ev.type,
        "owner": ev.owner,
        "actor": ev.actor,
        "created_at": ev.created_at,
        "headers": dict(ev.headers),
        "content": ev.content,
    }
    return d


def read_owner_events(memory_dir: Path, owner: str, actors: Optional[Iterable[str]] = None) -> List[Event]:
    """Read and parse all events for a space owner across selected actors."""
    events: List[Event] = []
    base = Path(memory_dir) / owner
    if not base.exists():
        return []
    for actor_dir in sorted(p for p in base.iterdir() if p.is_dir()):
        actor = actor_dir.name
        if actors is not None and actor not in actors:
            continue
        f = actor_dir / "activity.md"
        if not f.exists():
            continue
        try:
            txt = f.read_text(encoding="utf-8")
            events.extend(parse_blocks(txt))
        except Exception:
            continue
    return events


def index_by_id(events: Iterable[Event]) -> Dict[str, Event]:
    return {e.id: e for e in events if e.id}
